<?php
/**
*   Accept log messages, add necessary info, and forward to Writer
*
*   System Default is to have 2 log keepers: full log (textfile) and error log (stderr).
*   Other log keepers can be added in config.
*
*/
use Yz_Log_Interface as ILog;

class Yz_Log_Log
    extends Yz_Class
    implements ILog
{


    /**
    *   @var self::
    */
    protected static $instance;

    /**
    *   @var (string)
    */
    protected $logId;

    /**
    *   The minimum verbosity level when it is necessary to generate log message
    *   @var int
    */
    protected $minCode = ILog::FATAL;

    /**
    *   @var array <N: ILogWriter>
    */
    protected $writers = array();

    /**
    *   @var array <N: code >
    */
    protected $writerCodes = array();


    /******************************
    ***      Public methods     ***
    *******************************/

    /**
    *   @return int code 0=OK
    */
    public static function init()
    {
        static::$instance = new static();
        return 0;
    }

    /**
    *
    *
    */
    private function __construct()
    {
        $logId = $this->identifyLogId();
        if ( null === $logId ) {
            $logId = YZ_TIME;
        }
        $this->logId = $logId;
    } // __construct


    /**
    *   @param Yz_Log_Writer_Interface writer
    *   @param int code : the maximum verbosity level at which the writer will write
    *   @return int=0
    */
    public static function addWriter( Yz_Log_Writer_Interface $wr, $code=ILog::FATAL )
    {
        $log = static::$instance;
        $log->writers[] = $wr;
        $log->writerCodes[] = $code;
        // update minimum verbosity level
        if ( $code > $log->minCode ) {
            $log->minCode = $code;
        }
        return 0;
    }


    /**
    *
    */
    public static function log( $message, $code=ILog::DEBUG4, $traceDepth=0 )
    {
        $log = static::$instance;

        if( ! $log->writers ) {
            return 2;
        }

        // don't generate message if the code is more verbose than any of the registered writers
        if( $code > $log->minCode ) {
            return 1;
        }

        $trace = debug_backtrace();
        // the caller has its trace at level [1]
        $ct = isset( $trace[$traceDepth+1] ) ? $trace[$traceDepth+1] : $trace[1];

        switch ( true ) {
            case array_key_exists( 'object', $ct ) :
                $text = get_class($ct['object']) . '.' . $ct['function'] . '()';
                break;
            case array_key_exists( 'class', $ct ) :
                $text = $ct['class'] . '::' . $ct['function'] . '()';
                break;
            case array_key_exists( 'function', $ct ) :
                $text = 'function ' . $ct['function'] . '()';
                break;
            default:
                $text = $ct['file'] . '@' . $ct['line'];
        }

        "" !== "$message" and $text .= ": " . $message;
        foreach ( $log->writers as $N => $wr ) {
            if ( $log->writerCodes[$N] >= $code ) {
                $wr->write( $log->logId, $text );
            }
        }

        return 0;
    } // log

    /**
    *
    */
    protected function identifyLogId()
    {
        // read LOGID from file
        $idfile = YZ_LIB . 'app-id.log';
        if ( is_file($idfile) && is_writable($idfile) ) {
            $fp = fopen( $idfile, 'r+b' );
            if( $fp ) {
                flock( $fp, LOCK_EX );
                $id = fgets($fp);
                $id = 1 + (int)$id;
                fseek( $fp, 0, SEEK_SET );
                fputs( $fp, $id );
                flock( $fp, LOCK_UN );
                fclose( $fp );
                return $id;
            }
        }
        return null;
    }


} // class


